package org.momo.rpc.client.impl;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.momo.rpc.circuitbreaker.impl.CircuitBreaker;
import org.momo.rpc.protocol.RpcMessage;
import org.momo.rpc.protocol.RpcResponse;

import java.net.InetSocketAddress;


@Slf4j
public class NettyClientHandler extends SimpleChannelInboundHandler<RpcMessage<RpcResponse>> {

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        log.debug("Connect to server {}:{} success", remoteAddress.getHostName(), remoteAddress.getPort());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        log.debug("Server {}:{} close the channel", remoteAddress.getHostName(), remoteAddress.getPort());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, RpcMessage<RpcResponse> resMessage) throws Exception {
        log.debug("Client receive a response: {}", resMessage);

        Long id = resMessage.getHeader().getId();
        UnprocessedRequest unProcessedReq = UnprocessedRequest.get(id);

        // the request does not exist
        if (unProcessedReq == null) {
            log.error("Client receive an unknown message! id: {}", id);
            return;
        }

        // fill the reception result
        UnprocessedRequest.remove(id);
        unProcessedReq.getResultFuture().complete(resMessage);

        // report the result of request to circuit breaker
        CircuitBreaker circuitBreaker = unProcessedReq.getCircuitBreaker();
        if(resMessage.getHeader().getStatus() == 0) {
            circuitBreaker.recordSuccess();
        } else {
            circuitBreaker.recordFailure();
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            if (state == IdleState.WRITER_IDLE) {
//                InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
//                log.info("Client channel write idle happen! channel remote address: {}, port: {}", remoteAddress.getAddress(), remoteAddress.getPort());
//                RpcMessage<RpcRequest> pingMessage = RpcMessage.heartBeatRequest();
//                ctx.writeAndFlush(pingMessage);
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
}
